﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;

namespace Luka;

/// <summary>
/// Swagger操作类
/// </summary>
[NotScanned]
public static class SwaggerSetup
{
    /// <summary>
    /// 添加swagger
    /// </summary>
    public static IServiceCollection AddSwagger(this IServiceCollection services)
    {
        var appsetting = InternalApp.GetSection<SwaggerOptions>("Swagger");
        services.AddSwaggerGen(setupAction: options =>
        {
            #region 样式/分组

            var groups = GetSwaggerGroup();
            foreach (var item in groups)
            {
                //标题去除版本信息
                var title = item.GroupName;
                if (!string.IsNullOrWhiteSpace(item.Version) && item.GroupName?.EndsWith($"<{item.Version}>") == true)
                {
                    title = item.GroupName.Replace($"<{item.Version}>", string.Empty);
                }
                //文档信息
                options.SwaggerDoc(item.GroupName, new OpenApiInfo
                {
                    Version = item.Version ?? "1.0",
                    Title = title,
                    Description = (!string.IsNullOrEmpty(item.Description) ? $"描述信息：{item.Description}<br>" : "") +
                    $"当前环境：{InternalApp.EnvironmentName}<br>"
                });
            }
            //判断接口归于哪个分组
            options.DocInclusionPredicate((docName, apiDesc) =>
            {
                if (docName == "NoGroup")
                {
                    return string.IsNullOrEmpty(apiDesc.GroupName);
                }
                else
                {
                    if (!apiDesc.TryGetMethodInfo(out var methodInfo))
                    {
                        return false;
                    }

                    var versions = methodInfo.DeclaringType?.GetCustomAttributes(true).OfType<ApiDescriptionAttribute>()
                        .Select(attr => attr?.Version)
                        .Where(v => !string.IsNullOrWhiteSpace(v));

                    if (versions != null && versions.Any())
                    {
                        return versions.Any(v => $"{apiDesc.GroupName}<{v}>" == docName);
                    }
                    else if (!string.IsNullOrWhiteSpace(apiDesc.GroupName))
                    {
                        return apiDesc.GroupName == docName;
                    }
                    else if (groups.Count() == 1 && string.IsNullOrWhiteSpace(apiDesc.GroupName))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            });
            //添加中文注释 true 展示控制器注释
            if (appsetting?.XmlName != null && appsetting.XmlName.Length > 0)
            {
                foreach (var item in appsetting.XmlName)
                {
                    options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{item}.xml"), true);
                }
            }
            //其他配置
            if (appsetting != null)
            {
                //忽略废弃属性
                options.SchemaGeneratorOptions.IgnoreObsoleteProperties = appsetting.IgnoreObsoleteProperties;
                //忽略废弃方法
                options.SwaggerGeneratorOptions.IgnoreObsoleteActions = appsetting.IgnoreObsoleteActions;
                //解决同类名报错
                if (appsetting.IsÄhnlicheName == true)
                    options.CustomSchemaIds(type => type.FullName);
                //根据方法方法名称排序
                if (appsetting.IsOrderActions == true)
                    options.OrderActionsBy(it => it.RelativePath);

                #region 接口访问的域名和前缀（虚拟路径）以方便访问不同地址的接口（注意设置跨域）

                var okApis = appsetting.OpenApis?.Where(it => it.IsEnable == true).ToList();
                if (okApis != null && okApis?.Count > 0)
                {
                    foreach (var item in okApis)
                    {
                        options.AddServer(new OpenApiServer { Url = item.Url, Description = item.Description });
                    }
                }

                #endregion 接口访问的域名和前缀（虚拟路径）以方便访问不同地址的接口（注意设置跨域）

                #region jwt

                if (appsetting.EnableAuth)
                {
                    //添加安全定义
                    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                    {
                        Description = "请输入token,格式为 Bearer xxxxxxxx（注意中间必须有空格）",
                        Name = "Authorization",
                        In = ParameterLocation.Header,
                        Type = SecuritySchemeType.ApiKey,
                        BearerFormat = "JWT",
                        Scheme = "Bearer"
                    });
                    //添加安全要求
                    options.AddSecurityRequirement(new OpenApiSecurityRequirement {
                        {
                            new OpenApiSecurityScheme{
                                Reference =new OpenApiReference{
                                    Type = ReferenceType.SecurityScheme,
                                    Id ="Bearer"
                                }
                            },Array.Empty<string>()
                        }
                    });
                }

                #endregion jwt
            }

            #endregion 样式/分组
        });

        return services;
    }

    /// <summary>
    /// 使用
    /// </summary>
    public static IApplicationBuilder UseSwaggerBuilder(this IApplicationBuilder app)
    {
        SwaggerOptions? appsetting = InternalApp.GetSection<SwaggerOptions>("Swagger") ?? new SwaggerOptions();
        if (!appsetting.IsEnable)
        {
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                var groups = GetSwaggerGroup();
                foreach (var item in groups)
                {
                    c.SwaggerEndpoint($"/swagger/{item.GroupName}/swagger.json", item.GroupName);
                }
                //展示样式
                c.DocExpansion(appsetting.Expansion);
                //默认路由
                c.RoutePrefix = string.Empty;
            });
        }
        return app;
    }

    /// <summary>
    /// 获取swagger分组
    /// </summary>
    /// <returns></returns>
    private static IEnumerable<ApiDescriptionAttribute> GetSwaggerGroup()
    {
        var types = InternalApp.TypeList.Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToList();
        var groups = new List<ApiDescriptionAttribute>();
        foreach (var item in types)
        {
            var data = item.GetCustomAttribute<ApiDescriptionAttribute>();
            if (data != null && !string.IsNullOrWhiteSpace(data.GroupName))
            {
                if (!string.IsNullOrWhiteSpace(data.Version))
                {
                    data.GroupName += $"<{data.Version}>";
                }
                groups.Add(data);
            }
            else if (!groups.Any(it => !string.IsNullOrEmpty(it.GroupName) && it.GroupName.Equals("NoGroup")))
            {
                groups.Add(new ApiDescriptionAttribute { GroupName = "NoGroup", Order = 99999 });
            }
        }
        if (groups.Count == 1 && groups.Any(it => !string.IsNullOrEmpty(it.GroupName) && it.GroupName.Equals("NoGroup")))
        {
            groups[0] = new ApiDescriptionAttribute { GroupName = InternalApp.ProgramName };
        }
        var list = groups.Where(it => !string.IsNullOrWhiteSpace(it.GroupName))
            .Where(it => !it.IgnoreApi)
            .OrderBy(it => it.Order)
            .ThenBy(it => it.Version)
            .DistinctBy(it => it.GroupName);

        return list;
    }
}